# **************************************
# --*-- coding: utf-8 --*--
# @Author  : white
# @FileName: main.py
# @Time    : 2025-09-05
# **************************************
import numpy as np  # 导入NumPy库，用于数值计算

# 生成数据集：2个类别，每个类别100个样本，使用两个高斯分布模拟
# 类别1：均值[0, 0]，标准差1
# 类别2：均值[5, 5]，标准差1
# 数据维度为2维，便于可视化
data_class1 = np.random.normal(loc=[0, 0], scale=1, size=(100, 2))  # 生成类别1的100个样本
data_class2 = np.random.normal(loc=[5, 5], scale=1, size=(100, 2))  # 生成类别2的100个样本
data = np.vstack((data_class1, data_class2))  # 垂直堆叠成一个数据集，形状(200, 2)

# K-Means参数设置
K = 2  # 簇数为2   簇的数量
# 簇大小是算法运行后的输出结果，表示每个簇包含的样本数。簇大小之和等于总样本数（200），但每个簇的大小不一定相等。
# 例如，可能是簇0有80个样本，簇1有120个样本。
# 在你的数据生成方式中（两个高斯分布，均值分别为[0, 0]和[5, 5]，各100个样本），理想情况下每个簇的大小接近100，
# 但由于K-Means的随机初始化和迭代过程，实际簇大小可能略有偏差。
max_iters = 100  # 最大迭代次数
tolerance = 1e-4  # 收敛阈值，如果中心变化小于此值则停止

# 步骤1: 随机初始化K个簇中心，从数据中随机选择
indices = np.random.choice(data.shape[0], K, replace=False)  # 随机选择K个不重复的索引
centers = data[indices]  # 初始化簇中心

# 迭代过程
for iter in range(max_iters):
    # 步骤2: 分配每个数据点到最近的簇中心
    distances = np.zeros((data.shape[0], K))  # 初始化距离矩阵，形状(200, 2)
    for k in range(K):
        distances[:, k] = np.linalg.norm(data - centers[k], axis=1)  # 计算每个点到第k个中心的欧氏距离
    labels = np.argmin(distances, axis=1)  # 为每个点分配最近簇的标签（0或1）

    # 步骤3: 更新簇中心为每个簇内点的均值
    new_centers = np.zeros((K, data.shape[1]))  # 初始化新中心矩阵
    for k in range(K):
        cluster_points = data[labels == k]  # 提取属于第k簇的点
        if len(cluster_points) > 0:  # 避免空簇
            new_centers[k] = np.mean(cluster_points, axis=0)  # 计算均值作为新中心
        else:
            new_centers[k] = centers[k]  # 如果空簇，保持原中心

    # 步骤4: 检查收敛，如果所有中心变化小于阈值则停止
    center_shift = np.linalg.norm(new_centers - centers, axis=1)  # 计算每个中心的变化
    centers = new_centers  # 更新中心
    if np.all(center_shift < tolerance):
        print(f"算法在第 {iter + 1} 次迭代收敛")  # 输出收敛信息
        break

# 输出最终结果
print("最终簇中心：")
print(centers)  # 打印最终簇中心
print("样本标签（前10个）：")
print(labels[:10])  # 打印前10个样本的簇标签作为示例
